/*******************************************************\
|* Author: Trevor Sundberg
\*******************************************************/

// Header protection
#pragma once
#ifndef SKUGO_DELEGATE_HPP
#define SKUGO_DELEGATE_HPP

namespace Skugo
{
  template <typename Arg1>
  class Delegate
  {
  public:

    // Create a delegate from a method and an instance of the object
    template <typename Class, void (Class::*InstanceMethod)(Arg1)>
    static Delegate FromInstanceMethod(Class* self)
    {
      return Delegate(self, &WrapInstanceMethod<Class, InstanceMethod>);
    }
  
    // Create a delegate from a static method
    template <void (*StaticMethod)(Arg1)>
    static Delegate FromStaticMethod()
    {
      return Delegate(NULL, &WrapStaticMethod<StaticMethod>);
    }
  
    // Create a delegate from a static method
    template <void (*StaticMethod)(Arg1, void*)>
    static Delegate FromStaticMethod(void* userData)
    {
      return Delegate(userData, &WrapStaticMethod<StaticMethod>);
    }

    // Invoke the delegate
    void operator ()(Arg1 a1) const
    {
      // Internally just invoke the wrapper
      return mWrapper(mSelf, a1);
    }
  
  private:
  
    // The wrapper function we use to change function pointers into a single signature
    typedef void (*WrapperFn)(void* self, Arg1 a1);

    // Constructor
    Delegate(void* self, WrapperFn wrapper)
    {
      // Store the self pointer and the wrapper function
      mSelf = self;
      mWrapper = wrapper;
    }

    // Wrap a member function into a particular signature
    template <typename Class, void (Class::*InstanceMethod)(Arg1)>
    static void WrapInstanceMethod(void* self, Arg1 a1)
    {
      // Cast the self object back into its original type
      Class* selfTyped = static_cast<Class*>(self);
    
      // Invoke the member function pointer
      return (selfTyped->*InstanceMethod)(a1);
    }
  
    // Wrap a static function into a particular signature
    template <void (*StaticMethod)(Arg1)>
    static void WrapStaticMethod(void* /*self*/, Arg1 a1)
    {
      // Invoke the static function pointer
      return (StaticMethod)(a1);
    }
  
    // Wrap a static function into a particular signature
    template <void (*StaticMethod)(Arg1, void*)>
    static void WrapStaticMethod(void* userData, Arg1 a1)
    {
      // Invoke the static function pointer
      return (StaticMethod)(a1, userData);
    }

  private:

    // The self object. In instance methods, this is the 'this'
    // pointer and in statioc methods this should always be NULL
    void* mSelf;
  
    // The wrapper encompases the instance/static method into one signature
    WrapperFn mWrapper;
  };
}

// End header protection
#endif
